#include <e.h>
#include "e_mod_main.h"



static E_Config_DD *_core_config_entry=NULL;
static E_Config_DD *_widget_config_entry=NULL;




static Core *core;

#define BUFFER_SIZE 1024
static char *bufferstring;
/*
 ****************************************************************************************************** 
 *		Widget specs 
 * 
 ****************************************************************************************************** 
 */
 
 static Widget *_widget_new(E_Module *m,E_Gadcon * gc, const char *name, const char *id, const char *style)
{
	E_Gadcon_Client *gcc;
	Widget *w;
	Core *c;
	if (!m)
		return;
	if (!m->data)
		return;
	c=m->data;
	Evas_Object *o, *of, *ob;
	int i=0;
	w = E_NEW (Widget, 1);
	w->m=m;
	w->conf=_core_widget_config_get(c,id);
	if (!_widget_theme_init(w,gc->evas))
	{
		e_module_dialog_show(w->m,D_(MODULE_NAME"Desktop Widget"), D_("Theme init failed!"));
		_widget_free(w);
		return NULL;
	};
        
	gcc = e_gadcon_client_new (gc, name, id, style, w->canvas);
	gcc->data = w;
	w->gcc = gcc;
	evas_object_event_callback_add (w->canvas, EVAS_CALLBACK_MOUSE_DOWN,
				  _widget_cb_mouse_down, w);	
	return w;
}

static void _widget_free(Widget *w)
{
	if (w)
	{
		if (w->config_dialog != NULL)
    		e_object_del (E_OBJECT (w->config_dialog));		
  		evas_object_event_callback_del (w->canvas, EVAS_CALLBACK_MOUSE_DOWN,_widget_cb_mouse_down);
		_widget_theme_shutdown(w);
		E_FREE(w);
	}	
}

static int	_widget_theme_init(Widget *w, Evas *evas)
{
	char buf[PATH_MAX];
	if (!w->canvas)
	{
		if (evas)
		{
			w->canvas = edje_object_add (evas);
		}
		else
		{
			return 0;
		}
	}
	else
	{
		evas_object_hide(w->canvas);
	}
	snprintf (buf, sizeof (buf), "%s/themes/%s",e_module_dir_get (w->m),w->conf->theme);
	if (!edje_object_file_set (w->canvas, buf, "modules/"MODULE_NAME"/main"))
    {getutent
    	snprintf (buf, sizeof (buf), "%s/themes/%s",e_module_dir_get (w->m),DEFAULT_THEME);
    	if (!edje_object_file_set (w->canvas, buf, "modules/"MODULE_NAME"/main"))
    		return 0;
    	e_module_dialog_show(w->m,D_(MODULE_NAME" Desktop Widget"), D_ ("e17.net.module Theme init <hilight> failed!</hilight><br> Loaded e17.net.module default Theme."));	
    }
/*
 * UnPremul ARG 		(Color << 8)/Alpha
 * Premul ARG			(Color*Alpha) >> 8
 * Each time color should be passed in premul 
 * if we want to detect rgb we should call evas_object_color_get and manualy unpremul them
 */  	
  	int tmp;
  	tmp=((255*w->conf->alpha) >> 8);
  	evas_object_color_set(w->canvas,tmp,tmp,tmp, w->conf->alpha);
  	evas_object_show(w->canvas);
  	return 1;	
}

static void _widget_theme_shutdown(Widget *w)
{
	if (w->canvas)
	{
		evas_object_del(w->canvas);
		w->canvas=NULL;	
	}
}

static void _widget_cb_mouse_down (void *data, Evas * e, Evas_Object * obj, void *event_info)
{
  Widget *w;
  Evas_Event_Mouse_Down *ev;
  w = data;
  ev = event_info;
  if (ev->button == 3)getutent
    {
      E_Menu *mn;
      E_Menu_Item *mi;
      int x, y, width, height;

      mn = e_menu_new ();
      e_menu_post_deactivate_callback_set (mn, _widget_menu_cb_post, w);
      w->menu = mn;

      mi = e_menu_item_new (mn);
      e_menu_item_label_set (mi, D_("Configuration"));
      e_util_menu_item_edje_icon_set (mi, "enlightenment/configuration");
      e_menu_item_callback_set (mi, _widget_menu_cb_configure, w);

      mi = e_menu_item_new (mn);
      e_menu_item_separator_set (mi, 1);

      e_gadcon_client_util_menu_items_append (w->gcc, mn, 0);
      e_gadcon_canvas_zone_geometry_get (w->gcc->gadcon, &x, &y, &width, &height);
      e_menu_activate_mouse (mn,
			     e_util_zone_current_get (e_manager_current_get
						      ()), x + ev->output.x,
			     y + ev->output.y, 1, 1,
			     E_MENU_POP_DIRECTION_DOWN, ev->timestamp);
      evas_event_feed_mouse_up (w->gcc->gadcon->evas, ev->button,
				EVAS_BUTTON_NONE, ev->timestamp, NULL);
    }
}

static void _widget_menu_cb_configure (void *data, E_Menu * m, E_Menu_Item * mi)
{
	Widget *w;
	w = data;
	if (!w)
		return;
	if (w->config_dialog!=NULL)  
		return;	
	w->config_dialog= _widget_config(w);
}

static void _widget_menu_cb_post (void *data, E_Menu * m)
{
	Widget *w;		
	w = data;
}

void _uptime_widget_config_updated(Widget *w)
{
	if (!w)
		return;	
	_widget_theme_init(w,NULL);
}


/*
 ****************************************************************************************************** 
 * Core Specs
 * 
 ****************************************************************************************************** 
 */


static Core *_core_new(void)
{
	Core *c;
	bufferstring=calloc(1,BUFFER_SIZE*sizeof(char));
	c = E_NEW(Core, 1);
   	if (!c)
   	  return NULL;
   	if (!_core_config_load(c))
   	{
   		
   	}
   	e_gadcon_provider_register(&_gc_class);
   	
   	  c->boottime=_get_boottime();
#ifdef __FreeBSD__
  	if ((c->kd = kvm_open("/dev/null", "/dev/null", "/dev/null", O_RDONLY, "kvm_open"))==NULL)
  	{
  	
  	}
#endif

  	c->pagesize=getpagesize();
  	return c;
   	
}

static void _core_free(Core *c)
{
	if (!c)
		return;
	if (c->config_dialog != NULL)
    	e_object_del (E_OBJECT (c->config_dialog));
	if (c->timer!=NULL)
		ecore_timer_del (c->timer);
	if (c->conf->theme)
		evas_stringshare_del(c->conf->theme);	
	while (c->conf->items)
    {
		Widget_Config *wc;
		wc = c->conf->items->data;
		c->conf->items = evas_list_remove_list (c->conf->items, c->conf->items);
		if (wc->id != NULL)
			evas_stringshare_del(wc->id);
		if (wc->theme!=NULL)
			evas_stringshare_del(wc->theme);
      	E_FREE (wc);
    }
	e_gadcon_provider_unregister (&_gc_class);
#ifdef __FreeBSD__
    kvm_close(c->kd);
#endif
	E_FREE(c->conf);
	E_FREE(bufferstring);
	E_FREE(c);
	E_CONFIG_DD_FREE (_widget_config_entry);
	E_CONFIG_DD_FREE (_core_config_entry);
}

static int _core_config_save(Core *c)
{
	if (!c)
		return;
  	Evas_List *l;
	Widget *w;
    Widget_Config *wc;
	for (l = c->widgets; l; l = l->next)
    {
		w = l->data;
		wc = _core_widget_config_get(c,w->gcc->id);
		if (wc->id)
			evas_stringshare_del (wc->id);
		wc->id = evas_stringshare_add(w->gcc->id);
	}
	e_config_domain_save (MODULE_NAME, _core_config_entry, c->conf);
	return 1;
}

static int _core_config_load(Core *c)
{
	if (!c)
		return;
		
	_widget_config_entry = E_CONFIG_DD_NEW (MODULE_NAME".widget", Widget_Config);
#undef T
#undef D
#define T Widget_Config
#define D _widget_config_entry
	E_CONFIG_VAL (D, T, id, STR);
	E_CONFIG_VAL (D, T, theme, STR);
	E_CONFIG_VAL (D, T, alpha, INT);
#undef T
#undef D
	_core_config_entry = E_CONFIG_DD_NEW (MODULE_NAME".core",Core_Config);
#define T Core_Config
#define D _core_config_entry
	E_CONFIG_VAL (D, T, version, INT);
	E_CONFIG_VAL (D, T, theme, STR);
	E_CONFIG_VAL (D, T, alpha, INT);
	E_CONFIG_VAL (D, T, poll_time, INT);
	E_CONFIG_LIST (D, T, items, _widget_config_entry);
	c->conf = e_config_domain_load (MODULE_NAME, _core_config_entry);
	if (c->conf == NULL)
    {
		c->conf = E_NEW (Core_Config, 1);
		c->conf->version = CONFIG_VERSION;
		c->conf->poll_time=1.0;
		c->conf->theme=evas_stringshare_add(DEFAULT_THEME);
		c->conf->alpha=255;
    }
}

static int _core_cb_update(void *data)
{
	Core *c;
 	Widget *w;
	Evas_List *l;
	time_t uptime;
	int days, hours, minutes;
	struct load_average la;
	int nusers;
	static char *load_avg;  
	static char *users;
	static char *u_date_time;

	c=data;
 	if (!c)
 	{
 		return;
 	}

	if (c->widgets)
	{
		uptime=_get_uptime(c->boottime);	
  		if (uptime != -1)
    	{
    		days = uptime / (24 * 3600);
    		uptime %= 24 * 3600;
   	
	    	hours = uptime / 3600;
    		uptime %= 3600;

    		minutes = uptime / 60;
     		uptime %= 60;
      		if (days == 0) 
      		{
				snprintf (bufferstring, BUFFER_SIZE,D_ ("%d:%02d:%02ld"),hours, minutes, uptime);
	  		}
      		else
        		if (days == 1)
	  			{
					snprintf (bufferstring,  BUFFER_SIZE,D_ ("%d day ,%d:%02d:%02ld"),days,hours, minutes, uptime);
	  			}
				else     
	  			{
					snprintf (bufferstring,  BUFFER_SIZE,D_ ("%d days ,%d:%02d:%02ld"),days,hours, minutes, uptime);
	  			}
	  		u_date_time=strdup(bufferstring);
    	}
		
#ifdef __FreeBSD__
		la=_get_load_average(c->kd);
#else
		la=_get_load_average();
#endif
  		if (la.la_one != -1)
    	{
    		snprintf (bufferstring,  BUFFER_SIZE,D_ ("%.2f %.2f %.2f"),la.la_one, la.la_five, la.la_fifteen);
    		load_avg=strdup(bufferstring);
    	}
    	nusers=_get_nusers();
  		if (nusers != -1)
  		{
    		snprintf (bufferstring,  BUFFER_SIZE, D_ ("%d"), nusers);
    		users=strdup(bufferstring);
  		}
  		snprintf (bufferstring,  BUFFER_SIZE, D_ ("<header>System is up for:</header> "
    											"<contents>%s </contents>"
    									   "<header>Load Average: </header>"
    									   		"<contents>%s </contents>"								   		
    									   "<header>Users Logged In: </header>"
    									   		"<contents>%s</contents>"),u_date_time,load_avg,users);
		for (l = core->widgets; l; l = l->next)
	    	{
		  		w = l->data;
		  		if (w)
		  			{
	    			//	strftime (bufferstring, BUFFER_SIZE, w->conf->format, local_time);
		 				edje_object_part_text_set (w->canvas, MODULE_NAME".text", bufferstring);
		  			}
		  		else continue;		
	    	}
	}
	E_FREE(users); 
	E_FREE(u_date_time);			
	E_FREE(load_avg);
	
	/*
	 * returned 1 then continue calling timer 
	 * return 0 abort 
	 */

	return 1;	
}

static Widget *_core_widget_add(Core *c,E_Gadcon * gc, const char *name, const char *id, const char *style)
{
	Widget *w;
	if (!c)
		return NULL;
	if (c->timer)
	{
		ecore_timer_del(c->timer);
		c->timer=NULL;
	}
	w=_widget_new(c->m,gc,name,id,style);
	if (!w)
		return NULL;
	c->widgets =evas_list_append(c->widgets, w);
	c->timer=ecore_timer_add (c->conf->poll_time, _core_cb_update, c);
	_core_cb_update(c);
  	return w;		
}

static Widget_Config *_core_widget_config_get(Core *c, const char *id)
{
	Evas_List *l;
	Widget_Config *wc;
	if (!c)
		return NULL;
	for (l = c->conf->items; l; l = l->next)
	{
		wc = l->data;
		if (wc->id == NULL)
			continue;
		if (strcmp (wc->id, id) == 0)
			return wc;
    }
    wc = E_NEW (Widget_Config, 1);
    wc->id = evas_stringshare_add (id);
    wc->theme = evas_stringshare_add(c->conf->theme);
    wc->alpha = c->conf->alpha;
    c->conf->items = evas_list_append (c->conf->items, wc);
  return wc;	
}
static void _core_widget_delete(Core *c, Widget *w)
{
	if (!c)
		return;
	if (c->timer)
	{
		ecore_timer_del(c->timer);
		c->timer=NULL;
		
	}		
	if (!w)
		return;
	c->widgets = evas_list_remove(c->widgets, w);
	if (c->widgets)
	{
		c->timer=ecore_timer_add (c->conf->poll_time, _core_cb_update, c);
	}	
	_widget_free(w);
}

void _uptime_core_config_updated(Core *c)
{
	if (!c)
		return;
}

/*
 ****************************************************************************************************** 
 *		Gadcon specs 
 * 
 ****************************************************************************************************** 
 */
 
 static E_Gadcon_Client *_gc_init (E_Gadcon * gc, const char *name,const char *id, const char *style)
{
	Widget *w;
	w = _core_widget_add(core,gc,name,id,style);
	if (!w)
		return NULL;
	else
		return w->gcc;
}


static void _gc_shutdown (E_Gadcon_Client * gcc)
{
	E_Module *m;
	Core *c;
	Widget *w;
	if (!gcc)
		return;
	w = gcc->data;
	if (!w)
		return;
	m=w->m;
	if (!m)	
		return;
	c=m->data;
	if (!c)
		return;
	_core_widget_delete(c,w);
  
}
static void _gc_orient (E_Gadcon_Client * gcc)
{
	Widget *w;
	Evas_Coord width, height;
	w = gcc->data;
	if (!w)
		return;
	switch (gcc->gadcon->orient)
	{
		case E_GADCON_ORIENT_FLOAT:
		case E_GADCON_ORIENT_HORIZ:
		case E_GADCON_ORIENT_VERT:
		case E_GADCON_ORIENT_LEFT:
		case E_GADCON_ORIENT_RIGHT:
		case E_GADCON_ORIENT_TOP:
		case E_GADCON_ORIENT_BOTTOM:
		case E_GADCON_ORIENT_CORNER_TL:
		case E_GADCON_ORIENT_CORNER_TR:
		case E_GADCON_ORIENT_CORNER_BL:
		case E_GADCON_ORIENT_CORNER_BR:
		case E_GADCON_ORIENT_CORNER_LT:
		case E_GADCON_ORIENT_CORNER_RT:
		case E_GADCON_ORIENT_CORNER_LB:
		case E_GADCON_ORIENT_CORNER_RB:
			width = 16;
			height = 16;
		default:
		break;
	}
	
	edje_object_size_min_calc (w->canvas, &width, &height);
	e_gadcon_client_min_size_set (gcc, width, height);
}
static char *_gc_label (void)
{
	return MODULE_NAME;
}
static Evas_Object *_gc_icon (Evas * evas)
{
	Evas_Object *o;
	char buf[4096];
	o = edje_object_add (evas);
	snprintf (buf, sizeof (buf), "%s/data/module.edj",e_module_dir_get(core->m));
	edje_object_file_set (o, buf, "icon");
	return o;
}

/*
 ****************************************************************************************************** 
 * E_MODULE_API Staff 
 * dl directly look into module.so and fill references
 ****************************************************************************************************** 
 */
 
EAPI E_Module_Api e_modapi = 
   {
      E_MODULE_API_VERSION,
      MODULE_NAME
   };
   
EAPI void *e_modapi_init (E_Module *m)
{
//  Core *c;
  bindtextdomain(PACKAGE, LOCALEDIR);
  bind_textdomain_codeset (PACKAGE, "UTF-8");
  core = _core_new();
  core->m = m;
  m->data = core;
  return core;
}

EAPI int
e_modapi_config(E_Module *m)
{
   Core *c;
   if (!m) return 0;
   if (!m->data) return 0;
   c=m->data;	
   if (c->config_dialog)
   		return 0;
   c->config_dialog=_core_config(c);
   return 1;
}


EAPI int e_modapi_shutdown (E_Module *m)
{
	Core *c;
	c = m->data;
	if (c)
	   _core_free(c);
  return 1;
}

EAPI int e_modapi_save (E_Module *m)
{
  Core *c;
  c = m->data;
  if(c)
  	{
  		_core_config_save(c);
  	}
  return 1;
}

EAPI int e_modapi_about (E_Module *m)
{
  e_module_dialog_show (m, D_ ("Enlightenment Clock Module"),
			D_ ("This module is used to show System Time."));
  return 1;
}
/*
 * 
 * 		Utility Functions
 * 
 * 
 */
/* FreeBSD */
#ifdef __FreeBSD__
static time_t _get_boottime()
{
  int mib[2] = { CTL_KERN, KERN_BOOTTIME };
  struct timeval boottime;
  size_t size;
  size = sizeof (boottime);
  if ((sysctl (mib, 2, &boottime, &size, NULL, 0) != -1) && (boottime.tv_sec != 0))
    {
      return boottime.tv_sec;
    }
  else
    return -1;
}

static int _get_nusers()
{
	int users;
	FILE *utmp;
  	struct timespec tp;
  	struct utmp ut;
  	char ttybuf[MAXPATHLEN];
  	struct stat st;
  	utmp = fopen (_PATH_UTMP, "r");
  	if (utmp == NULL)
    	users = -1;
  	else
    {
      	for (users = 0; fread (&ut, sizeof (ut), 1, utmp);)
		{
	  		if (ut.ut_name[0] == '\0')
	    		continue;
	  		(void) snprintf (ttybuf, sizeof (ttybuf), "%s%.*s", _PATH_DEV,UT_LINESIZE, ut.ut_line);
	  		if (stat (ttybuf, &st) != 0)
	    		continue;		/* broken record */
	  		users++;
		}
      fclose (utmp);
    }
  return users;  
}

static struct load_average _get_load_average(kvm_t *kd)
{
	int ret;
	struct load_average la;
	double l[3];
 	ret = kvm_getloadavg (kd, l, sizeof (l) / sizeof (l[0]));
  	if (ret < 0)
  	{  	
  		warn ("getloadavg()");
  		la.la_one=-1;
  		return la;
  	}
  	else
  	{
  		la.la_one=l[0];
  		la.la_five=l[1];
  		la.la_fifteen=l[2];
  		return la;
  	}
}
#endif

#ifdef __linux__
static time_t _get_boottime()
{
	struct sysinfo s_info;
	time_t now;
	sysinfo (&s_info);
	time(&now);
	return (now-s_info.uptime);
}
static int _get_nusers()
{
	struct utmp *utmpstruct;
	int users=0;
	setutent ();
  	while ((utmpstruct = getutent ()))
    {
      if ((utmpstruct->ut_type == USER_PROCESS) && (utmpstruct->ut_name[0] != '\0'))
		users++;
    }
  	endutent ();
}

static struct load_average _get_load_average()
{
	int ret=-1;
	struct load_average la;
#ifdef HAVE_GETLOADAVG
  	ret = getloadavg (la, sizeof (la) / sizeof (la.la_one));
# endif
  	if (ret < 0)
  	{
    	la.la_one= -1;
    	return la;
  	}
  	else 
  	{
  		return la;
  	}
}
#endif

static time_t _get_uptime(int boottime)
{
	
  	time_t uptime;
  	time_t now;
	if (boottime==-1)
	{
    	return -1;
    }
  	else
  	{
  		(void) time (&now);
  		uptime = now - boottime;
      	if (uptime > 60)
			uptime += 30;
		return uptime;	
  	}
}
